package com.symone.crawler.service;

import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.symone.crawler.App;
import com.symone.crawler.action.Catcher;
import com.symone.crawler.config.AirBnbConfig;
import com.symone.crawler.config.CrawlerConfig;
import com.symone.crawler.data.EntityMapper;
import com.symone.crawler.data.ProxyData;
import com.symone.crawler.log.Log;
import com.symone.crawler.log.LogFactory;
import com.symone.crawler.util.EntityUtil;
import org.jsoup.Connection.Response;

import java.sql.SQLException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class InfoService {

    private static Log logger = LogFactory.getLog(InfoService.class);
    private volatile Catcher catcher = new Catcher();

    private Entity room;
    private ProxyData proxy;
    private Map<String,String> params;

    public InfoService(Entity room){
        super();
        this.room = room;
        params = new HashMap<>();
        params.put("key", AirBnbConfig.key);
    }

    public void checkRoom(boolean useProxy){
        synchronized (this) {
            try {
                Response response = null;
                if(useProxy){

                    proxy = App.PROXY_THREAD_MAP.get(Thread.currentThread().getId());
                    if(proxy == null || proxy.getExpireTime().before(new Date())){
                        proxy = ProxyService.getProxyData();
                        logger.info("PROXY [" + proxy.getIp() + ":"  + proxy.getPort() + "    -   "  + CrawlerConfig.DATETIME_FORMATTER.format(proxy.getExpireTime()) + "] is Using ......");
                    }
                    try {
                        response = catcher.catchDocumentWithProxy(AirBnbConfig.INFO_URL + room.getStr("id"), params, proxy);
                        //logger.info("-------------------  RETRY " + retry + " - " +reIndex);
                    } catch (Exception ex) {
                        //ex.printStackTrace();
                        logger.error("--->  获取数据失败!" + ex.getMessage());
                        room.set("check_code", 999);
                        room.set("check_time", CrawlerConfig.SIMPLE_DATETIME_FORMATTER.format(new Date()));
                        Db.use().update(room, Entity.create("airbnb_room").set("id", this.room.getLong("id")));
                        Thread.sleep((long) 1000);
                        //Thread.sleep((long) (1000 * ((Math.random() * 5 + 1))));
                    }

                }else{
                    response = catcher.catchDocument(AirBnbConfig.INFO_URL  + room.getStr("id"), params);
                }

                room.set("check_code", response.statusCode());
                room.set("check_time", CrawlerConfig.SIMPLE_DATETIME_FORMATTER.format(new Date()));
                Db.use().update(room, Entity.create("airbnb_room").set("id", this.room.getLong("id")));
            } catch (Exception e) {
                //e.printStackTrace();
                logger.error("获取数据失败!" + e.getMessage());
            }
        }
    }

    public void saveWithFetch(boolean useProxy) {
        synchronized (this) {
            try {
                String result = null;
                if(useProxy){
                    int reIndex = 0;
                    while (true) {
                        if(reIndex >= CrawlerConfig.MAX_RETRY){
                            logger.error("URL 重试多次访问失败! URL:" + AirBnbConfig.INFO_URL + room.getStr("id") + " PARAMS:" + JSONUtil.toJsonStr(params));
                            return;
                        }
                        proxy = App.PROXY_THREAD_MAP.get(Thread.currentThread().getId());
                        if(proxy == null || proxy.getExpireTime().before(new Date())){
                            proxy = ProxyService.getProxyData();
                            logger.info("PROXY [" + proxy.getIp() + ":"  + proxy.getPort() + "    -   "  + CrawlerConfig.DATETIME_FORMATTER.format(proxy.getExpireTime()) + "] is Using ......");
                        }
                        try {
                            result = catcher.catchDocumentWithProxy(AirBnbConfig.INFO_URL  + room.getStr("id"), this.params, proxy).body();
                            //logger.info("-------------------  RETRY " + retry + " - " +reIndex);
                        } catch (Exception ex) {
                            //ex.printStackTrace();
                            logger.error("--->  RETRY [ " + reIndex + " ] 获取数据失败!" + ex.getMessage());
                            Thread.sleep((long) 1000);
                        }
                        if( result != null){
                            break;
                        }
                        reIndex ++;
                    }
                }else{
                    result = catcher.catchDocument(AirBnbConfig.INFO_URL  + room.getStr("id"), params).body();
                }
                saveData(result);
            } catch (Exception e) {
                e.printStackTrace();
                //logger.error("获取数据失败!" + e.getMessage());
            } finally {
                //logger.info("总次数/成功次数/失败次数："+ total +"/"+success + "/"+failed);
            }
        }
    }

    private void saveData(String json){
        JSONObject roomJson = JSONUtil.parseObj(json).getJSONObject("listing");

        if(roomJson == null){
            logger.info("Room ID [ " + room.getStr("id") + " ] NO DATA ...");
            Integer errorCode = JSONUtil.parseObj(json).getInt("error_code");
            if(errorCode != null){
                try {
                    Db.use().del(new Entity("airbnb_room").set("id",room.getStr("id")));
                } catch (SQLException ex) {
                    ex.printStackTrace();
                    logger.error(ex.getMessage());
                }
            }
            return;
        }

        EntityMapper roomMapper = EntityMapper.create("airbnb_room")
                .setFields("access",
                        "address",
                        "bathrooms",
                        "beds",
                        "bed_type",
                        "bed_type_category",
                        "bedrooms",
                        "calendar_updated_at",
                        "cancellation_policy",
                        "cancel_policy",
                        "cancel_policy_short_str",
                        "check_in_time",
                        "check_in_time_end",
                        "check_in_time_ends_at",
                        "check_in_time_start",
                        "check_out_time",
                        "city",
                        "Shanghai",
                        "cleaning_fee_native",
                        "country",
                        "country_code",
                        "description",
                        "description_locale",
                        "experiences_offered",
                        "extras_price_native",
                        "force_mobile_legal_modal",
                        "formatted_check_out_time",
                        "guests_included",
                        "has_agreed_to_legal_terms",
                        "has_double_blind_reviews",
                        "has_license",
                        "has_viewed_cleaning",
                        "has_viewed_ib_perf_dashboard_panel",
                        "has_viewed_terms",
                        "house_rules",
                        "id",
                        "in_building",
                        "in_landlord_partnership",
                        "instant_bookable",
                        "instant_book_welcome_message",
                        "interaction",
                        "in_toto_area",
                        "is_location_exact",
                        "language",
                        "access",
                        "lat",
                        "lng",
                        "license",
                        "locale",
                        "localized_check_in_time_window",
                        "localized_check_out_time",
                        "localized_city",
                        "map_image_url",
                        "market",
                        "max_nights",
                        "max_nights_input_value",
                        "min_nights",
                        "min_nights_input_value",
                        "monthly_price_factor",
                        "name",
                        "native_currency",
                        "neighborhood",
                        "neighborhood_overview",
                        "notes",
                        "person_capacity",
                        "picture_count",
                        "picture_url",
                        "price",
                        "price_for_extra_person_native",
                        "price_native",
                        "property_type",
                        "property_type_id",
                        "public_address",
                        "require_guest_phone_verification",
                        "require_guest_profile_picture",
                        "requires_license",
                        "reviews_count",
                        "room_type",
                        "room_type_category",
                        "security_deposit_formatted",
                        "security_deposit_native",
                        "security_price_native",
                        "smart_location",
                        "space",
                        "square_feet",
                        "star_rating",
                        "state",
                        "summary",
                        "time_zone_name",
                        "transit",
                        "user_id",
                        "weekly_price_factor",
                        "zipcode")
                .setPks("id");
        EntityUtil.save(roomJson, roomMapper);

        JSONObject userJson = roomJson.getJSONObject("user").getJSONObject("user");
        EntityMapper userMapper = EntityMapper.create("airbnb_user")
                .setFields("id",
                        "first_name",
                        "last_name",
                        "picture_url",
                        "has_profile_pic",
                        "created_at",
                        "reviewee_count",
                        "recommendation_count",
                        "response_rate",
                        "response_time",
                        "acceptance_rate",
                        "wishlists_count",
                        "publicly_visible_wishlists_count",
                        "is_superhost")
                .setPks("id");
        EntityUtil.save(userJson,userMapper);

        JSONObject gcJson = roomJson.getJSONObject("guest_controls");
        EntityMapper gcMapper = EntityMapper.create("airbnb_room_guest_control")
                .setFields("allows_children_as_host",
                        "allows_infants_as_host",
                        "allows_pets_as_host",
                        "allows_smoking_as_host",
                        "allows_events_as_host",
                        "children_not_allowed_details",
                        "structured_house_rules");
        EntityUtil.save(gcJson,gcMapper,"room_id",room.getStr("id").toString());

        JSONArray amenityIdsJson = roomJson.getJSONArray("amenities_ids");
        JSONArray amenitiesJson = roomJson.getJSONArray("amenities");
        for (int i = 0 ; i < amenityIdsJson.size() ; i ++){
            Entity amenitiesEntity = Entity.create("airbnb_room_amenity");
            amenitiesEntity.set("room_id",room.getStr("id"));
            amenitiesEntity.set("amenity_id",amenityIdsJson.getLong(i));
            try {
                if(Db.use().count(amenitiesEntity) == 0 ) {
                    amenitiesEntity.set("amenity_name",amenitiesJson.getStr(i));
                    Db.use().insert(amenitiesEntity);
                }
            } catch (SQLException ex) {
                ex.printStackTrace();
                logger.error(ex.getMessage());
            }
        }
    }
}
